package com.hyt.it.ogt.data.mq;

import com.alibaba.fastjson.JSON;
import com.hyt.it.ogt.common.config.RedisKeyConstant;
import com.hyt.it.ogt.data.model.entity.kw.PushPj;
import com.hyt.it.ogt.data.model.entity.pj.Project;
import com.hyt.it.ogt.data.service.kw.IPushPjService;
import com.hyt.it.ogt.data.service.pj.IPjDataProjectService;
import com.rabbitmq.client.Channel;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 推送评卷数据消费者
 *
 * @author liuqi
 * @date 2022-05-31
 */
@Component
@Slf4j
public class PushPjDataConsumer {

    private final static int LOCK_TIME = 10;

    @Resource
    private IPjDataProjectService iPjDataProjectService;
    @Resource
    IPushPjService iPushPjService;
    @Resource
    private RedissonClient redissonClient;

    @SneakyThrows
    @RabbitListener(bindings = {
            @QueueBinding(value = @Queue(value = "${mq.pushPjData.queue}", autoDelete = "false", durable = "true"), 
                    exchange = @Exchange(value = "${mq.kzToKhDirect}", type = ExchangeTypes.DIRECT), 
                    key = { "${mq.pushPjData.route}" }) }, 
                    concurrency = "${mq.pushPjData.concurrency}")
    public void pushPjDataListener(String json, Message message, Channel channel) {
        log.info("[推送评卷]消费推送评卷数据:【{}】", json);
        PushPj pushPj = JSON.parseObject(json, PushPj.class);
        Long pushId = pushPj.getId();
        String projectId = pushPj.getExamId();
        String officeId = pushPj.getOfficeId();
        String deptId = pushPj.getDeptId();
        String admissionNum = pushPj.getAdmissionNum();
        //1.初始化评卷项目
        Project project = iPjDataProjectService.getById(projectId);
        if (project == null) {
            //获取初始化项目锁
            RLock redisLock = redissonClient.getLock(RedisKeyConstant.PUSH_PJ_DATA_INIT_PROJECT + projectId);
            //获取锁成功，执行初始化项目
            try {
                // 尝试获取锁等待10S
                boolean lockSuccess = redisLock.tryLock(LOCK_TIME, TimeUnit.SECONDS);
                if (lockSuccess) {
                    project = iPjDataProjectService.getById(projectId);
                    if (project == null) {
                        iPjDataProjectService.initProjectForTask(projectId, officeId, deptId);
                    } else {
                        iPjDataProjectService.updateProjectForTask(projectId, officeId);
                    }
                } else {
                    log.debug("[推送评卷]消费评卷推送数据等待未获得锁，修改为未推送状态->{}", json);
                    //评卷项目正在其它消费者初始化，推送任务改为未推送状态，等待下一轮定时任务推送
                    iPushPjService.updateStatus(pushId, 0);
                    channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                }
            } catch (InterruptedException e) {
                log.error("[推送评卷]初始化评卷项目异常:", e);
                //评卷项目初始化异常，推送任务改为初始化异常状态
                iPushPjService.updateStatus(pushId, 0, e.getMessage());
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                Thread.currentThread().interrupt();
            } catch (Exception e) {
                log.error("[推送评卷]初始化评卷项目异常:", e);
                //评卷项目初始化异常，推送任务改为初始化异常状态
                iPushPjService.updateStatus(pushId, 0, e.getMessage());
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            } finally {
                //释放锁
                redisLock.unlock();
            }
        } else {
            iPjDataProjectService.updateProjectForTask(projectId, officeId);
        }

        //2.推送考生评卷数据
        try {
            iPjDataProjectService.addProjectStudentForTask(projectId, officeId, admissionNum);
            //删除记录
            iPushPjService.delete(pushId);
        } catch (Exception e) {
            log.error("[推送评卷]推送考生评卷数据异常:", e);
            iPushPjService.updateStatus(pushId, 2, e.getMessage());
        } finally {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }
    }

}
